SQLiteは軽量ですが、UIスレッドで実行すると簡単にフリーズします。 特に WinForms / WPF / MAUI では、非同期処理(async/await)が必須です。 この記事では、SQLiteを非同期で扱うための最速・実務向けパターンをまとめています。
この記事でわかること
・SQLiteの非同期処理が必要な理由
・ExecuteReaderAsync / ExecuteNonQueryAsync の使い方
・DataTableを非同期で読み込む方法
・Dapper/EF Coreの非同期対応
・業務アプリ向けベストプラクティス
・SQLiteの非同期処理が必要な理由
・ExecuteReaderAsync / ExecuteNonQueryAsync の使い方
・DataTableを非同期で読み込む方法
・Dapper/EF Coreの非同期対応
・業務アプリ向けベストプラクティス
1. なぜSQLiteで非同期が必要なのか?
- SQLiteは単一ファイルDB → ロックが発生しやすい
- 読み込み中にUIが固まる(特にDataGridView)
- 大量データのSELECTで数秒固まることもある
UIスレッドでDBアクセスをしない これがSQLiteアプリの鉄則です。
2. 非同期接続(OpenAsync)
using Microsoft.Data.Sqlite;
var cs = "Data Source=sample.db";
using var con = new SqliteConnection(cs);
await con.OpenAsync();
ポイント
- OpenAsync() は必ず await する
- UIスレッドをブロックしない
3. 非同期SELECT(ExecuteReaderAsync)
var sql = "SELECT Id, Name, Age FROM Users";
using var cmd = new SqliteCommand(sql, con);
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var id = reader.GetInt32(0);
var name = reader.GetString(1);
var age = reader.IsDBNull(2) ? (int?)null : reader.GetInt32(2);
Console.WriteLine($"{id}, {name}, {age}");
}
ポイント
- ExecuteReaderAsync()
- ReadAsync()
- UIフリーズを完全回避
4. 非同期INSERT / UPDATE / DELETE(ExecuteNonQueryAsync)
var sql = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";
using var cmd = new SqliteCommand(sql, con);
cmd.Parameters.AddWithValue("@name", "Taro");
cmd.Parameters.AddWithValue("@age", 25);
await cmd.ExecuteNonQueryAsync();
UPDATE / DELETE も同じ
await cmd.ExecuteNonQueryAsync();
5. DataTableを非同期で読み込む(業務アプリ向け)
DataTable.Load() は同期処理なので、 Task.Run でバックグラウンド化するのが実務パターン。
var dt = new DataTable();
await Task.Run(async () =>
{
using var cmd = new SqliteCommand("SELECT * FROM Users", con);
using var reader = await cmd.ExecuteReaderAsync();
dt.Load(reader);
});
// DataGridView.DataSource = dt;
ポイント
- DataTable.Load は同期 → Task.Run で逃がす
- UIスレッドを完全にブロックしない
6. Dapperの非同期(QueryAsync / ExecuteAsync)
var users = (await con.QueryAsync<User>(
"SELECT * FROM Users"
)).ToList();
INSERT
await con.ExecuteAsync(
"INSERT INTO Users (Name, Age) VALUES (@Name, @Age)",
new { Name = "Taro", Age = 25 }
);
7. EF Core の非同期(ToListAsync / FirstOrDefaultAsync)
var users = await db.Users
.Where(x => x.Age >= 20)
.ToListAsync();
INSERT
db.Users.Add(user);
await db.SaveChangesAsync();
8. 非同期処理の注意点(SQLite特有)
SQLiteは同時書き込みに弱い
→ 非同期でも「同時に複数書き込み」は避ける
- 書き込みはキュー化する(1つずつ)
- 読み込みは複数同時でもOK(基本的に)
- 大量INSERTはトランザクション必須
9. 業務アプリでのベストプラクティス
- UIスレッドでDBアクセスしない
- DataTable.Load は Task.Run で逃がす
- 書き込みは1つずつ(ロック対策)
- 大量処理はトランザクション+非同期
- Dapper/EF Core の非同期APIを積極活用
まとめ:SQLite × 非同期は“UIフリーズ対策の必須技術”
- OpenAsync / ExecuteReaderAsync / ExecuteNonQueryAsync が基本
- DataTableは Task.Run で非同期化
- Dapper / EF Core も非同期対応済み
- 書き込みは直列化してロック回避
SQLiteは軽量で扱いやすいですが、 非同期化しないとUIが固まるという弱点があります。 この記事をベースに、快適でストレスのないアプリを構築してください。